//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-06-30     ZhangYihua   first version
//
// Description  : 
//################################################################################

module rdat_mux #(
parameter           DW                      = 1,
parameter           ALL_NUM                 = 100,
parameter           SUB_NUM                 = 256       // SUB_NUM>=0, if SUB_NUM==0, means rdat is combinational logic output
) ( 
input                                       rst_n,      // unused when SUB_NUM==0
input                                       clk,        // unused when SUB_NUM==0

input               [ALL_NUM-1:0]           cs_ary,
input               [ALL_NUM*DW-1:0]        rdat_ary,

input                                       re,         // unused when SUB_NUM==0
output                                      rbusy,      // unused when SUB_NUM==0
output              [DW-1:0]                rdat
);

//################################################################################
// define local varialbe and localparam
//################################################################################

//################################################################################
// main
//################################################################################

generate if (SUB_NUM==0) begin:G_COMB
    reg                 [DW-1:0]                rdat_c;

    always@(*) begin:MUX
        integer i;

        for (i=0; i<ALL_NUM; i=i+1) begin
            if (i==0)
                rdat_c = (rdat_ary[i*DW+:DW] & {DW{cs_ary[i]}});
            else
                rdat_c = (rdat_ary[i*DW+:DW] & {DW{cs_ary[i]}}) | rdat_c;
        end
    end

    assign rbusy = 1'b0;
    assign rdat = rdat_c;
end else begin:G_REG
    localparam          L0_NUM                  = ALL_NUM/SUB_NUM + (((ALL_NUM%SUB_NUM)==0) ? 0 : 1);

    wire                                        rd_hit_l0;
    reg                 [L0_NUM*DW-1:0]         rdat_l0_c;
    reg                 [L0_NUM*DW-1:0]         rdat_l0;

    always@(*) begin:MUX
        integer i,j,k;

        for (i=0; i<ALL_NUM; i=i+1) begin
            j = i/SUB_NUM;
            k = i%SUB_NUM;

            if (k==0)
                rdat_l0_c[j*DW+:DW] = (rdat_ary[i*DW+:DW] & {DW{cs_ary[i]}});
            else
                rdat_l0_c[j*DW+:DW] = (rdat_ary[i*DW+:DW] & {DW{cs_ary[i]}}) | rdat_l0_c[j*DW+:DW];
        end
    end

    assign rd_hit_l0 = re;
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            rdat_l0 <=`U_DLY {L0_NUM*DW{1'b0}};
        end else begin
            if (rd_hit_l0==1'b1)
                rdat_l0 <=`U_DLY rdat_l0_c;
            else
                ;
        end
    end

    if (L0_NUM==1) begin:G_L0
        assign rbusy = 1'b0;
        assign rdat = rdat_l0;
    end else begin:G_L1
        localparam          L1_NUM                  = L0_NUM/SUB_NUM + (((L0_NUM%SUB_NUM)==0) ? 0 : 1);

        reg                                         rd_hit_l1;
        reg                 [L1_NUM*DW-1:0]         rdat_l1_c;
        reg                 [L1_NUM*DW-1:0]         rdat_l1;

        always@(*) begin:MUX
            integer i,j,k;

            for (i=0; i<L0_NUM; i=i+1) begin
                j = i/SUB_NUM;
                k = i%SUB_NUM;

                if (k==0)
                    rdat_l1_c[j*DW+:DW] = rdat_l0[i*DW+:DW];
                else
                    rdat_l1_c[j*DW+:DW] = rdat_l0[i*DW+:DW] | rdat_l1_c[j*DW+:DW];
            end
        end

        always@(posedge clk or negedge rst_n) begin
            if (rst_n==1'b0) begin
                rd_hit_l1 <=`U_DLY 1'b0;
            end else begin
                rd_hit_l1 <=`U_DLY rd_hit_l0;
            end
        end

        always@(posedge clk or negedge rst_n) begin
            if (rst_n==1'b0) begin
                rdat_l1 <=`U_DLY {L1_NUM*DW{1'b0}};
            end else begin
                if (rd_hit_l1==1'b1)
                    rdat_l1 <=`U_DLY rdat_l1_c;
                else
                    ;
            end
        end

        if (L1_NUM==1) begin
            assign rbusy = rd_hit_l1;
            assign rdat  = rdat_l1;
        end else begin:L2
            localparam          L2_NUM                  = L1_NUM/SUB_NUM + (((L1_NUM%SUB_NUM)==0) ? 0 : 1);

            reg                                         rd_hit_l2;
            reg                 [L2_NUM*DW-1:0]         rdat_l2_c;
            reg                 [L2_NUM*DW-1:0]         rdat_l2;

            always@(*) begin:MUX
                integer i,j,k;

                for (i=0; i<L1_NUM; i=i+1) begin
                    j = i/SUB_NUM;
                    k = i%SUB_NUM;

                    if (k==0)
                        rdat_l2_c[j*DW+:DW] = rdat_l1[i*DW+:DW];
                    else
                        rdat_l2_c[j*DW+:DW] = rdat_l1[i*DW+:DW] | rdat_l2_c[j*DW+:DW];
                end
            end

            always@(posedge clk or negedge rst_n) begin
                if (rst_n==1'b0) begin
                    rd_hit_l2 <=`U_DLY 1'b0;
                end else begin
                    rd_hit_l2 <=`U_DLY rd_hit_l1;
                end
            end

            always@(posedge clk or negedge rst_n) begin
                if (rst_n==1'b0) begin
                    rdat_l2 <=`U_DLY {L2_NUM*DW{1'b0}};
                end else begin
                    if (rd_hit_l2==1'b1)
                        rdat_l2 <=`U_DLY rdat_l2_c;
                    else
                        ;
                end
            end

            if (L2_NUM==1) begin
                assign rbusy = rd_hit_l2 | rd_hit_l1;
                assign rdat  = rdat_l2;
            end else begin:NULL
    `ifdef PARAMETER_NULL_CHECK  
                num_null u_num_null();
    `endif
            end
        end
    end
end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
